FreeCAD использует собственную систему на основе XML для создания Python-обёрток для классов C++. Чтобы обернуть класс C++ для использования в Python, необходимо создать два файла вручную, и два файла автоматически генерируются системой сборки CMake (в дополнение к заголовочным файлам C++ и файлам реализации класса).
Вам нужно создать:
[YourClass]Py.xml
[YourClass]PyImp.cpp
Отредактируйте соответствующий файл CMakeLists.txt, чтобы добавить ссылки на эти два файла. На основе XML-файла система сборки создаст:
[YourClass]Py.cpp
[YourClass]Py.h
XML-файл [YourClass]Py.xml
содержит информацию о функциях и атрибутах, которые реализует класс Python, а также пользовательскую документацию для этих элементов, которая отображается в консоли Python FreeCAD.
В этом примере мы рассмотрим обёртку для класса Axis C++. Файл описания XML начинается со следующего:
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="PyObjectBase"
Name="AxisPy"
Twin="Axis"
TwinPointer="Axis"
Include="Base/Axis.h"
FatherInclude="Base/PyObjectBase.h"
Namespace="Base"
Constructor="true"
Delete="true"
FatherNamespace="Base">
<Documentation>
<Author Licence="LGPL" Name="Juergen Riegel" EMail="FreeCAD@juergen-riegel.net" />
<UserDocu>User documentation here
</UserDocu>
<DeveloperDocu>Developer documentation here</DeveloperDocu>
</Documentation>
После этой преамбулы приводится список методов и атрибутов. Формат метода следующий:
<Methode Name="move">
<Documentation>
<UserDocu>
move(Vector)
Move the axis base along the vector
</UserDocu>
</Documentation>
</Methode>
Формат атрибута следующий:
<Attribute Name="Direction" ReadOnly="false">
<Documentation>
<UserDocu>Direction vector of the Axis</UserDocu>
</Documentation>
<Parameter Name="Direction" Type="Object" />
</Attribute>
Если атрибут "ReadOnly" имеет значение false, вы должны предоставить функцию getter и setter. Если значение равно true, то допускается только getter. В этом случае мы должны будем предоставить две функции в файле реализации C++:
Py::Object AxisPy::getDirection(void) const
и:
void AxisPy::setDirection(Py::Object arg)
Файл реализации C++ [YourClass]PyImp.cpp
обеспечивает "клей", который соединяет структуры C++ и Python вместе, эффективно переводя с одного языка на другой. Система FreeCAD C++-to-Python предоставляет ряд классов C++, которые соотносятся с соответствующими типами Python. Наиболее фундаментальным из них является класс Py::Object
- редко создаваемый напрямую, этот класс обеспечивает основу дерева наследования и используется в качестве возвращаемого типа для любой функции, возвращающей данные Python.
Ваш файл реализации на C++ будет включать следующие файлы:
#include "PreCompiled.h"
#include "[YourClass].h"
// Inclusion of the generated files (generated out of [YourClass]Py.xml)
#include "[YourClass]Py.h"
#include "[YourClass]Py.cpp"
Конечно, вы можете включить и другие заголовки C++, необходимые для работы вашего кода.
Ваша реализация на C++ должна содержать определение функции PyInit: например, для обёртки класса Axis это
int AxisPy::PyInit(PyObject* args, PyObject* /*kwd*/)
Внутри этой функции вам, скорее всего, понадобится разобрать входящие в конструктор аргументы: наиболее важной функцией для этого является предоставляемая Python PyArg_ParseTuple
. Она принимает переданный список аргументов, дескриптор для ожидаемых аргументов, которые она должна разобрать, а также информацию о типе и месте хранения результатов разбора. Например:
PyObject* d;
if (PyArg_ParseTuple(args, "O!O", &(Base::VectorPy::Type), &o,
&(Base::VectorPy::Type), &d)) {
// NOTE: The first parameter defines the base (origin) and the second the direction.
*getAxisPtr() = Base::Axis(static_cast<Base::VectorPy*>(o)->value(),
static_cast<Base::VectorPy*>(d)->value());
return 0;
}
Полный список спецификаторов формата приведён в Документации Python C API. Обратите внимание, что также определено несколько связанных функций, которые позволяют использовать ключевые слова и т. д. Полный набор следующий:
PyAPI_FUNC(int) PyArg_Parse (PyObject *, const char *, ...);
PyAPI_FUNC(int) PyArg_ParseTuple (PyObject *, const char *, ...);
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords (PyObject *, PyObject *, const char *, char **, ...);
PyAPI_FUNC(int) PyArg_VaParse (PyObject *, const char *, va_list);
PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords (PyObject *, PyObject *, const char *, char **, va_list);